#include "ZenFrame.h"

CZenFrame::CZenFrame():m_pParameter(NULL)
{
	m_Yaw	= 0.0f;
	m_Pitch	= 0.0f;
	m_Roll	= 0.0f;

	m_vPosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	m_vVelocity = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

	D3DXMatrixIdentity(&m_mLocal);

	m_pObjectList		= NULL;
	m_pNext				= NULL;
	m_pChildFrameList	= NULL;
	m_pParentFrame		= NULL;
	m_pfnCallback		= NULL;
	
	m_bCallback			= FALSE;
}

CZenFrame::~CZenFrame()
{
	/*GEEN OBJECTEN GAAN VERWIJDEREN, DIE JE ZELF NIET AANGEMAAKT HEBT!
	CZenObject * pObject = m_pObjectList;
	CZenObject * pTemp = NULL;

	while(pObject)
	{
		pTemp = (CZenObject*)pObject->GetNext();
		free(pObject);
		pObject = pTemp;
	}
	*/
}

void CZenFrame::SetNext(CZenFrame * pNext)
{
	m_pNext = pNext;
}

CZenFrame * CZenFrame::GetNext()
{
	return m_pNext;
}

void CZenFrame::SetParent(CZenFrame * pParent)
{
	m_pParentFrame = pParent;
}

CZenFrame * CZenFrame::GetParent()
{
	return m_pParentFrame;
}

HRESULT CZenFrame::AddFrame(CZenFrame & NewFrame)
{
	// Make sure the new frame is valid
	if( !(&NewFrame))
	{
		zen.SetError( "Failed in attempt to add an invalid child frame" );
		return E_FAIL;
	}
	
	// Tell it whos da daddy
	NewFrame.SetParent( this );
	
	// If the child frame has not been create yet...
	if( !m_pChildFrameList )
	{
		// ...Set this new frame as the first in the list
		m_pChildFrameList = &NewFrame;
	}
	else
	{
		// ...The list already exists
		
		// Get a pointer to the start of the list
		CZenFrame* pTempFrame = m_pChildFrameList;
		
		// Find the last entry in the list
		while( pTempFrame->GetNext() )
			pTempFrame = pTempFrame->GetNext();
		
		// Add this frame to the end of this list
		pTempFrame->SetNext( &NewFrame );
	}
	
	return S_OK;
}

HRESULT CZenFrame::AddFrame(CZenFrame * pNewFrame)
{
	// Make sure the new frame is valid
	if( !pNewFrame )
	{
		zen.SetError( "Failed in attempt to add an invalid child frame" );
		return E_FAIL;
	}
	
	// Tell it whos da daddy
	pNewFrame->SetParent( this );
	
	// If the child frame has not been create yet...
	if( !m_pChildFrameList )
	{
		// ...Set this new frame as the first in the list
		m_pChildFrameList = pNewFrame;
	}
	else
	{
		// ...The list already exists
		
		// Get a pointer to the start of the list
		CZenFrame* pTempFrame = m_pChildFrameList;
		
		// Find the last entry in the list
		while( pTempFrame->GetNext() )
			pTempFrame = pTempFrame->GetNext();
		
		// Add this frame to the end of this list
		pTempFrame->SetNext( pNewFrame );
	}
	
	return S_OK;
}

HRESULT CZenFrame::DeleteFrame(CZenFrame * pOldFrame)
{
	// Make sure the old frame is valid
	if( !pOldFrame )
	{
		zen.SetError( "Failed in attempt to remove an invalid child frame" );
		return E_FAIL;
	}

	// Make sure this frame has children
	if( !m_pChildFrameList )
	{
		zen.SetError( "The frame doesn't have any children" );
		return E_FAIL;
	}
	
	pOldFrame->SetParent(NULL);

	CZenFrame * pTempFrame = m_pChildFrameList;

	if( pOldFrame == pTempFrame )
	{
		m_pParentFrame->m_pChildFrameList = pOldFrame->GetNext();
	}
	else
	{
		while( pTempFrame->GetNext() )
		{
			if( pOldFrame == pTempFrame->GetNext() )
			{
				pTempFrame->SetNext(pOldFrame->GetNext());
				pOldFrame->SetNext(NULL);
				return S_OK;
			}
			pTempFrame = pTempFrame->GetNext();
		}
	}

	zen.SetError( "The old frame is not a child of this frame" );
	return E_FAIL;
}

HRESULT CZenFrame::DeleteFrame(CZenFrame & OldFrame)
{
	// Make sure the old frame is valid
	if( !(&OldFrame) )
	{
		zen.SetError( "Failed in attempt to remove an invalid child frame" );
		return E_FAIL;
	}

	// Make sure this frame has children
	if( !m_pChildFrameList )
	{
		zen.SetError( "The frame doesn't have any children" );
		return E_FAIL;
	}
	
	(&OldFrame)->SetParent(NULL);

	CZenFrame * pTempFrame = m_pChildFrameList;

	if( (&OldFrame) == pTempFrame )
	{
		m_pParentFrame->m_pChildFrameList = (&OldFrame)->GetNext();
	}
	else
	{
		while( pTempFrame->GetNext() )
		{
			if( (&OldFrame) == pTempFrame->GetNext() )
			{
				pTempFrame->SetNext((&OldFrame)->GetNext());
				(&OldFrame)->SetNext(NULL);
				return S_OK;
			}
			pTempFrame = pTempFrame->GetNext();
		}
	}

	zen.SetError( "The old frame is not a child of this frame" );
	return E_FAIL;
}

void CZenFrame::GetLocal(D3DXMATRIX &Matrix)
{
	Update();
	Matrix = m_mLocal;
}

void CZenFrame::GetVelocity(float &x, float &y, float &z)
{
	x = m_vVelocity.x;
	y = m_vVelocity.y;
	z = m_vVelocity.z;
}

void CZenFrame::SetVelocity(float x, float y, float z)
{
	m_vVelocity.x = x;
	m_vVelocity.y = y;
	m_vVelocity.z = z;
}

void CZenFrame::GetPosition(float &x, float &y, float &z)
{
	x = m_vPosition.x;
	y = m_vPosition.y;
	z = m_vPosition.z;
}

void CZenFrame::SetPosition(float x, float y, float z)
{
	m_vPosition.x = x;
	m_vPosition.y = y;
	m_vPosition.z = z;
}

void CZenFrame::SetPitch( float Pitch )
{
	m_Pitch = Pitch;
}

void CZenFrame::GetPitch( float& Pitch)
{ 
	Pitch = m_Pitch;
}

void CZenFrame::SetRoll( float Roll )
{
	m_Roll = Roll;
}

void CZenFrame::GetRoll( float& Roll)
{ 
	Roll = m_Roll;
}

void CZenFrame::SetYaw( float Yaw )
{
	m_Yaw = Yaw;  
}

void CZenFrame::GetYaw( float& Yaw)
{ 
	Yaw = m_Yaw; 
}

void CZenFrame::Update()
{
	if (m_bCallback)
		m_pfnCallback(this, m_pParameter);
	
	D3DXMATRIX mRotX, mRotY, mRotZ, mTrans, mRotTemp;

	m_vPosition.x += m_vVelocity.x;
	m_vPosition.y += m_vVelocity.y;
	m_vPosition.z += m_vVelocity.z;

	D3DXMatrixTranslation(&mTrans, m_vPosition.x, m_vPosition.y, m_vPosition.z);

	D3DXMatrixRotationX(&mRotX, m_Pitch);
	D3DXMatrixRotationY(&mRotY, m_Yaw);
	D3DXMatrixRotationZ(&mRotZ, m_Roll);

	D3DXMatrixMultiply(&mRotTemp, &mRotX, &mRotY);
	D3DXMatrixMultiply(&mRotTemp, &mRotZ, &mRotTemp);
	D3DXMatrixMultiply(&mTrans, &mRotTemp, &mTrans);

	m_mLocal = mTrans;

	if (GetParent())
	{
		D3DXMATRIX mParent;

		GetParent()->GetLocal(mParent);

		D3DXMatrixMultiply(&m_mLocal, &m_mLocal, &mParent);
	}

	zen.m_pDevice->SetTransform(D3DTS_WORLD, &mTrans);
}

HRESULT CZenFrame::SetCallback(FRAME_MOVEMENT_CALLBACK pfnCallback)
{
	if (!pfnCallback) 
	{
		m_bCallback		= TRUE;
		m_pfnCallback	= NULL;
		return E_FAIL;
	}

	m_pfnCallback = pfnCallback;
	m_bCallback = TRUE;

	return S_OK;
}

HRESULT CZenFrame::AddObject(CZenObject & NewObject)
{
	if (!(&NewObject))
		return E_FAIL;
	
	NewObject.SetParentFrame(this);

	if (!m_pObjectList)
		m_pObjectList = &NewObject;
	else
	{
		CZenObject * pObject = m_pObjectList;

		while(pObject->GetNext())
			pObject = (CZenObject*)pObject->GetNext();

		pObject->SetNext(&NewObject);
	}

	return S_OK;
}

HRESULT CZenFrame::AddObject(CZenObject * pNewObject)
{
	if (!pNewObject)
		return E_FAIL;
	
	pNewObject->SetParentFrame(this);

	if (!m_pObjectList)
		m_pObjectList = pNewObject;
	else
	{
		CZenObject * pObject = m_pObjectList;

		while(pObject->GetNext())
			pObject = (CZenObject*)pObject->GetNext();

		pObject->SetNext(pNewObject);
	}

	return S_OK;
}

HRESULT CZenFrame::Render()
{

	// Update the position and orientation of the frame
	// and set the new world transform matrix
	Update();

	CZenFrame* pFrame = m_pChildFrameList;

	while( pFrame )
	{
		pFrame->Render();
		pFrame = pFrame->GetNext();
	}
	
	// Return if this frame has no visuals to render
	if (!m_pObjectList)
		return S_OK;
	
	CZenObject * pObject = m_pObjectList;

	zen.m_pDevice->SetTransform(D3DTS_WORLD, &m_mLocal);

	while(pObject)
	{
		pObject->Render();
		pObject = (CZenObject*)pObject->GetNext();
	}

	return S_OK;
}
